﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BingVis.Common;
using BingVis.GoogleTransit;
using System.IO;
using System.ComponentModel;
using InfoStrat.VE;
using System.Windows;
using System.Threading;
using System.Diagnostics;
using System.Windows.Input;
using Microsoft.Win32;


namespace BingVis.WPFClient
{
    public class MainShellViewModel : ViewModelBase
    {
        private const string BASIC_HELP =
@"
MBTA Transit Stops are shown in the left hand side of the screen.  
- Stops with $ indicate stops that show fares
- Some stops are duplicated because of inconsistencies of the GTFS File (e.g. Inbound, Outbound stops).
- Select a stop by clicking on it's name.

Filter stops using the filter box above the stops list.
- Prepend or append your filter with $ to show only stops with fare data 
    - ( e.g. '$ back' to show stops matching 'back' with fares. )
- Use 'red', 'green', 'orange' or 'blue' for specific routes 
    - ('blue' for all blue line stops)

Fares are shown throughout the day in the bottom of the screen for a selected stop.
- Fares are grouped into 15 minute increments.

Fares for 8-12-2009 Fare Gate file are loaded by default
- Use the File menu to load other FareGate files. (Only FareGate files will populate Fares properly)

For more information, or to report a bug, visit http://mbtafarevis.codeplex.com
  
";
        private const string ABOUT_TEXT =
@"
This application was developed by Jacob Gable (http://jacob4u2.blogspot.com).

This application and it's source code is Licensed under the Microsoft Public License (Ms-PL).

It uses code from the Cinch MVVM Framework (http://cinch.codeplex.com) and the 
InfoStrat.VE Bing Maps control (http://virtualearthwpf.codeplex.com)

For more information, or to report a bug, visit http://mbtafarevis.codeplex.com.
";

        public StopsListViewModel StopsList { get; private set; }
        public FareListViewModel FaresList { get; private set; }

        private string testFilesDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles");

        private string stopsFilter;
        public string StopsFilter
        {
            get { return stopsFilter; }
            set
            {
                if (stopsFilter != value)
                {
                    stopsFilter = value;
                    OnPropertyChanged("StopsFilter");
                }
            }
        }

        public DelegateCommand<IGTFSFile> LoadStopFile { get; private set; }
        public DelegateCommand<object> ShowFareFileSelector { get; private set; }
        public DelegateCommand<object> ShowCompressedFareFileSelector { get; private set; }
        public DelegateCommand<object> ShowCompressFileSelector { get; private set; }
        public DelegateCommand<IGTFSFile> LoadFareFile{ get; private set; }        
        public DelegateCommand<IGTFSFile> LoadCompressedFareFile { get; private set; }
        public DelegateCommand<IGTFSFile> CompressFareFile { get; private set; }
        public DelegateCommand<object> ShowBasicHelp { get; private set; }
        public DelegateCommand<object> ShowAbout { get; private set; }        
        public DelegateCommand<KeyEventArgs> FilterOnEnter { get; private set; }
        public DelegateCommand<bool> ToggleShow3DBuildings { get; private set; }

        private IGTFSPackageLoader PackLoader { get; set; }
        private IFareTapLoader fareLoader { get; set; }

        private BackgroundWorker PackageWorker;

        private bool show3DBuildings;
        public bool Show3DBuildings
        {
            get { return show3DBuildings; }
            set
            {
                if (show3DBuildings != value)
                {
                    show3DBuildings = value;
                    OnPropertyChanged("Show3DBuildings");
                }
            }
        }

        private Visibility loadingVis;
        public Visibility LoadingVis
        {
            get { return loadingVis; }
            set
            {
                if (loadingVis != value)
                {
                    loadingVis = value;
                    OnPropertyChanged("LoadingVis");
                }
            }
        }

        private VELatLong startupLatLong;
        public VELatLong StartupLatLong
        {
            get { return startupLatLong; }
            set
            {
                if (startupLatLong != value)
                {
                    startupLatLong = value;
                    OnPropertyChanged("StartupLatLong");
                }
            }
        }
        private VELatLong currLatLong;
        public VELatLong CurrLatLong
        {
            get { return currLatLong; }
            set
            {
                if (currLatLong != value)
                {
                    currLatLong = value;
                    OnPropertyChanged("CurrLatLong");
                }
            }
        }
        private double viewAngle;
        public double ViewAngle
        {
            get { return viewAngle; }
            set
            {
                if (viewAngle != value)
                {
                    viewAngle = value;
                    OnPropertyChanged("ViewAngle");
                }
            }
        }

        public ITransitStop LastSelectedStop { get; private set; }
        
        public MainShellViewModel()
        {
            // TODO: Load these from config / settings?
            StartupLatLong = new VELatLong(42.3625, -71.0843, 2000);
            Show3DBuildings = false;
            CurrLatLong = new VELatLong(42.3625, -71.0843, 2000);

            ViewAngle = 55.00;

            StopsList = new StopsListViewModel();
            FaresList = new FareListViewModel();
            FaresList.CompressedFareFileFinishedLoading += new CompressedFareFileFinishedLoadingEvent(OnFaresListCompressedFareFileFinishedLoading);

            LoadingVis = Visibility.Hidden;

            
            LoadStopFile = new DelegateCommand<IGTFSFile>(OnLoadStopFile, false);
            ShowFareFileSelector = new DelegateCommand<object>(x => SelectFile("Select Fare File...", (y) => LoadFareFile.Execute(y)));
            ShowCompressedFareFileSelector = new DelegateCommand<object>(x => SelectFile("Select Compressed Fare File...", (y) => LoadCompressedFareFile.Execute(y)));
            ShowCompressFileSelector = new DelegateCommand<object>(x => SelectFile("Select Fare File to Compress...", (y) => CompressFareFile.Execute(y)));
            LoadFareFile = new DelegateCommand<IGTFSFile>(OnLoadFareFile, false);
            LoadCompressedFareFile = new DelegateCommand<IGTFSFile>(OnLoadCompressedFareFile, false);
            CompressFareFile = new DelegateCommand<IGTFSFile>(OnCompressFareFile, false);
            FilterOnEnter = new DelegateCommand<KeyEventArgs>(OnFilterStopsOnEnter, false);
            ToggleShow3DBuildings = new DelegateCommand<bool>(x => x = !x);

            ShowBasicHelp = new DelegateCommand<object>(x => MessageBox.Show(BASIC_HELP, "Basic Help", MessageBoxButton.OK));
            ShowAbout = new DelegateCommand<object>(x => MessageBox.Show(ABOUT_TEXT, "About MBTA Fare Visualization", MessageBoxButton.OK));

            mediator.Register(this);

            PackLoader = new PackageLoader();

            PackageWorker = new BackgroundWorker();
            PackageWorker.WorkerReportsProgress = false;
            PackageWorker.WorkerSupportsCancellation = false;
            PackageWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(PackageWorker_RunWorkerCompleted);
            PackageWorker.DoWork += new DoWorkEventHandler(PackageWorker_DoWork);

            // Load the default test data.
            OnLoadTestData(null);
        }

        private void OnFaresListCompressedFareFileFinishedLoading(object sender, EventArgs args)
        {
            // Load the last selected stop again to show the updated fare amounts.
            if (this.LastSelectedStop != null)
                OnStopSelected(this.LastSelectedStop);
        }

        private void SelectFile(string title, Action<IGTFSFile> fileAction)
        {
            OpenFileDialog d = new OpenFileDialog();
            d.Title = title;
            d.DefaultExt = "txt";
            d.CheckFileExists = true;
            d.CheckPathExists = true;
            d.Multiselect = false;

            var result = d.ShowDialog(App.Current.MainWindow);

            if (!result.HasValue || !result.Value)
                return;

            var f = new GTFSFile(new FileInfo(d.FileName));

            fileAction(f);
        }

        private void OnFilterStopsOnEnter(KeyEventArgs parm)
        {
            // Run the filter stops command on enter key.
            if (parm.Key == Key.Enter)
                StopsList.FilterStops.Execute(StopsFilter);
        }
      
        [Cinch.MediatorMessageSink(EventNames.SelectedStop, ParameterType=typeof(ITransitStop))]
        private void OnStopSelected(ITransitStop stop)
        {
            this.CurrLatLong = new VELatLong(stop.Latitude, stop.Longitude, 1000);
            LastSelectedStop = stop;

            if (FaresList.LoadedCompressedFares)
                FaresList.ShowFaresForStop.Execute(stop.Name);
        }
        
        private void OnLoadTestData(object parm)
        {
            PackageWorker.RunWorkerAsync(testFilesDirectory);
        }

        private void PackageWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = PackLoader.ParseFiles(new DirectoryFileLoader(e.Argument as string));
        }

        private void PackageWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            var files = e.Result as List<IGTFSFile>;
            if (files == null)
                // show an error?
                return;

            // Process the stops file.
            var stopFile = files.FirstOrDefault(x => x.FileName.Equals("stops.txt", StringComparison.CurrentCultureIgnoreCase));
            if (stopFile != null)
                LoadStopFile.Execute(stopFile);

            var compressedFareFile = files.FirstOrDefault(x => x.FileName.Contains("faresCompressed"));
            if (compressedFareFile != null)
                LoadCompressedFareFile.Execute(compressedFareFile);
        }

        private ITransitStopLoader stopLoader;
        private void OnLoadStopFile(IGTFSFile stopFile)
        {
            stopLoader = new TransitStopLoader(stopFile);
            WeakReference wr = new WeakReference(stopLoader);            
            stopLoader.ItemLoaded += new ItemLoadedEvent<ITransitStop>(OnStopLoaded);
            stopLoader.DoneProcessing += new DoneProcessingEvent(OnStopsFinishedLoading);
            stopLoader.ProcessFile();
        }

        private void OnStopLoaded(object sender, LoadedEventArgs<ITransitStop> args)
        {
            StopsList.AddStop.Execute(args.Item);
        }

        private void OnStopsFinishedLoading(object sender, DoneProcessingEventArgs args)
        {
            stopLoader.ItemLoaded -= new ItemLoadedEvent<ITransitStop>(OnStopLoaded);
            stopLoader.DoneProcessing -= new DoneProcessingEvent(OnStopsFinishedLoading);
            stopLoader.Items.Clear();
            stopLoader = null;
        }

        private void OnLoadFareFile(IGTFSFile fareFile)
        {
            // TODO: Signal that we are processing somehow...
            LoadingVis = Visibility.Visible;

            var newFileName = Path.GetFileNameWithoutExtension(fareFile.FullPath) + "_comp" + Path.GetExtension(fareFile.FullPath);
                        
            FareFileCompressor.CompressFareFile(fareFile.FullPath, Path.Combine(Path.GetDirectoryName(fareFile.FullPath), newFileName), 
                // Callback after compression is done.
                x => OnLoadCompressedFareFile(new GTFSFile(new FileInfo(x))));
        }

        private void OnLoadCompressedFareFile(IGTFSFile fareFile)
        {            
            FaresList.LoadCompressedFareFile.Execute(fareFile);
            LoadingVis = Visibility.Hidden;
        }

        private void OnCompressFareFile(IGTFSFile fareFile)
        {
            LoadingVis = Visibility.Visible;
            var newFileName = Path.GetFileNameWithoutExtension(fareFile.FullPath) + "_comp" + Path.GetExtension(fareFile.FullPath);
            FareFileCompressor
                .CompressFareFile(fareFile.FileName, 
                        Path.Combine(Path.GetDirectoryName(fareFile.FullPath), newFileName), 
                        x => { MessageBox.Show("Finished compressing file to: " + x); LoadingVis = Visibility.Hidden;});
        }

        //private void OnFareLoaded(object sender, LoadedEventArgs<IFareTap> args)
        //{
        //    FaresList.AddFare.Execute(args.Item);
        //}

        //private void OnFareDoneProcessing(object sender, DoneProcessingEventArgs args)
        //{
        //    LoadingVis = Visibility.Hidden;
        //}

        
    }
}
